package top.goldenyear.porpoise.framework.web.core.generator.model;

import cn.hutool.v7.core.bean.BeanUtil;
import cn.hutool.v7.core.text.StrUtil;
import com.jfinal.kit.Kv;
import java.util.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import top.goldenyear.porpoise.common.core.util.string.StrUtils;
import top.goldenyear.porpoise.framework.web.core.generator.config.DomainConfig;
import top.goldenyear.porpoise.framework.web.core.generator.config.GlobalConfig;
import top.goldenyear.porpoise.framework.web.core.generator.config.ModuleConfig;
import top.goldenyear.porpoise.framework.web.core.generator.config.TableConfig;

/**
 * 类信息
 *
 * @author airhead
 */
@Slf4j
@Data
public class ClassMeta {

  /** 类名 */
  private String className;

  private String comment;

  /** 字段 */
  private List<FieldMeta> fields = new ArrayList<>();

  private TableMeta tableMeta;
  private TableConfig tableConfig;
  private Kv kvTableConfig;

  private DomainConfig domainConfig;
  private ModuleConfig moduleConfig;
  private GlobalConfig globalConfig;

  public static ClassMeta covert(
      TableMeta tableMeta, GlobalConfig globalConfig, ModuleConfig moduleConfig) {
    ClassMeta classMeta = new ClassMeta();
    classMeta.setGlobalConfig(globalConfig);
    classMeta.setModuleConfig(moduleConfig);
    classMeta.setDomainConfig(
        globalConfig.getStrategyConfig().getDomainConfig(tableMeta.getName()));
    classMeta.setTableConfig(globalConfig.getStrategyConfig().getTableConfig(tableMeta.getName()));
    classMeta.setTableMeta(tableMeta);
    classMeta.buildClassName(tableMeta.getName());
    classMeta.setComment(tableMeta.getComment());

    List<ColumnMeta> columnMetas = tableMeta.getColumnMetas();
    for (ColumnMeta columnMeta : columnMetas) {
      FieldMeta fieldMeta = FieldMeta.covert(tableMeta, columnMeta, globalConfig);
      classMeta.getFields().add(fieldMeta);
    }

    return classMeta;
  }

  public void buildClassName(String tableName) {
    this.className = getClassName(tableName);
  }

  public String getClassName(String tableName) {
    if (domainConfig != null) {
      tableName = StrUtil.replace(tableName, domainConfig.getTablePrefix(), "");
    }

    return StrUtil.upperFirst(StrUtils.underlineToCamel(tableName));
  }

  public String getKebabName(String tableName) {
    if (domainConfig != null) {
      tableName = StrUtil.replace(tableName, domainConfig.getTablePrefix(), "");
    }

    return StrUtils.underlineToKebab(tableName);
  }

  public String getSubClassName() {
    return StrUtil.lowerFirst(this.className);
  }

  public String getDomain() {
    if (domainConfig == null) {
      return null;
    }

    return domainConfig.getDomain();
  }

  public String getBizDomain() {
    List<String> list = new ArrayList<>();
    String biz = getBiz();
    if (StrUtil.isNotBlank(biz)) {
      list.add(biz);
    }

    String domain = getDomain();
    if (StrUtil.isNotBlank(domain)) {
      list.add(domain);
    }

    return String.join("/", list);
  }

  public String getModule() {
    if (moduleConfig == null) {
      return null;
    }

    return moduleConfig.getModule();
  }

  public String getModuleClass() {
    return String.format(
        "%s%s%s", moduleConfig.getClassPrefix(), className, moduleConfig.getClassSuffix());
  }

  public String getModuleProxyClass() {
    return String.format(
        "%s%s%s",
        moduleConfig.getClassPrefix(), className, moduleConfig.getClassSuffix() + "Proxy");
  }

  public String getModuleVar() {
    return StrUtil.lowerFirst(getModuleClass());
  }

  public String getBasePackage() {
    return globalConfig.getPackageConfig().getBasePackage();
  }

  public String getBiz() {
    String biz = globalConfig.getPackageConfig().getBiz();
    if (StrUtil.isNotBlank(biz)) {
      return biz;
    }

    if (domainConfig == null) {
      return "";
    }

    return domainConfig.getBiz();
  }

  public String getParentPackage() {
    return getParentPackage(moduleConfig.getModule());
  }

  public String getModulePackage() {
    return getParentPackage() + "." + getModuleClass();
  }

  public String getModuleProxyPackage() {
    return getParentPackage() + ".proxy." + getModuleProxyClass();
  }

  public String getPackagePath() {
    return getParentPackage().replace(".", "/");
  }

  public String getModuleFile() {
    return getModulePackage().replace(".", "/") + moduleConfig.getFileSuffix();
  }

  public String getModuleKebab() {
    return StrUtils.camelToKebab(
        String.format(
            "%s%s%s", moduleConfig.getClassPrefix(), className, moduleConfig.getClassSuffix()));
  }

  public String getModuleKebabFile() {
    return getModuleKebab() + moduleConfig.getFileSuffix();
  }

  public String getModuleClass(String moduleName) {
    ModuleConfig config = getModuleConfig(moduleName);
    if (config == null) {
      log.error("未配置对应的模块, moduleName: {}", moduleName);
      return null;
    }

    return String.format("%s%s%s", config.getClassPrefix(), className, config.getClassSuffix());
  }

  public String getModuleClass(String moduleName, String table) {
    ModuleConfig config = getModuleConfig(moduleName);
    if (config == null) {
      log.error("未配置对应的模块, moduleName: {}", moduleName);
      return null;
    }

    return String.format(
        "%s%s%s", config.getClassPrefix(), getClassName(table), config.getClassSuffix());
  }

  /**
   * 连接符
   *
   * @param moduleName
   * @return
   */
  public String getModuleKebab(String moduleName) {
    return StrUtils.camelToKebab(getModuleClass(moduleName));
  }

  /**
   * 连接符
   *
   * @param moduleName
   * @return
   */
  public String getModuleKebab(String moduleName, String table) {
    return StrUtils.camelToKebab(getModuleClass(moduleName, table));
  }

  public String getModuleKebabFile(String moduleName) {
    ModuleConfig config = getModuleConfig(moduleName);

    return StrUtils.camelToKebab(
            String.format("%s%s%s", config.getClassPrefix(), className, config.getClassSuffix()))
        + config.getFileSuffix();
  }

  public String getModuleProxyClass(String moduleName) {
    ModuleConfig config = getModuleConfig(moduleName);
    return String.format(
        "%s%s%s", config.getClassPrefix(), className, config.getClassSuffix() + "Proxy");
  }

  public String getModuleVar(String moduleName) {
    return StrUtil.lowerFirst(getModuleClass(moduleName));
  }

  public String getParentPackage(String moduleName) {
    ModuleConfig config = getModuleConfig(moduleName);

    List<String> packages = new ArrayList<>();
    if (StrUtil.isNotBlank(getBasePackage())) {
      packages.add(getBasePackage());
    }

    if (StrUtil.isNotBlank(getBiz())) {
      packages.add(getBiz());
    }

    if (StrUtil.isNotBlank(getDomain())) {
      packages.add(getDomain());
    }

    if (StrUtil.isNotBlank(config.getModule())) {
      packages.add(config.getModule());
    }

    return String.join(".", packages);
  }

  public String getModulePackage(String moduleName) {
    ModuleConfig config = getModuleConfig(moduleName);
    return getParentPackage(moduleName) + "." + getModuleClass(moduleName);
  }

  public String getModuleProxyPackage(String moduleName) {
    ModuleConfig config = getModuleConfig(moduleName);
    return getParentPackage(moduleName) + ".proxy." + getModuleProxyClass(moduleName);
  }

  public String getSuperClass() {
    if (moduleConfig.getSuperClass() != null) {
      return moduleConfig.getSuperClass().getName();
    }

    return null;
  }

  public String getSuperClassSimple() {
    if (moduleConfig.getSuperClass() != null) {
      return moduleConfig.getSuperClass().getSimpleName();
    }

    return null;
  }

  public String getSuperInterface() {
    if (moduleConfig.getSuperInterface() != null) {
      return moduleConfig.getSuperInterface().getName();
    }

    return null;
  }

  public String getSuperInterfaceSimple() {
    if (moduleConfig.getSuperInterface() != null) {
      return moduleConfig.getSuperInterface().getSimpleName();
    }

    return null;
  }

  public String getTable() {
    return tableMeta.getName();
  }

  public String getComment() {
    return tableMeta.getComment();
  }

  public String getMappingPath() {
    List<String> mappingPaths = new ArrayList<>();
    if (StrUtil.isNotBlank(getBizDomain())) {
      mappingPaths.add(getBizDomain());
    }

    if (StrUtil.isNotBlank(getSubClassName())) {
      if (!getSubClassName().equals(getDomain())) {
        mappingPaths.add(getSubClassName());
      }
    }

    return String.join("/", mappingPaths);
  }

  /**
   * 属性增加的导入包
   *
   * @return
   */
  public List<String> getImports() {
    Set<String> imports = new TreeSet<>();
    for (FieldMeta field : fields) {
      imports.addAll(field.getImports());
    }
    return new ArrayList<>(imports);
  }

  /** 构建 extends 继承。 */
  public String buildExtends() {
    // TODO:
    return "";
  }

  /** 构建 implements 实现。 */
  public String buildImplements() {
    // TODO:
    return "";
  }

  public String getPkClass() {
    for (FieldMeta field : fields) {
      if (field.getColumnMeta().getPrimaryKey()) {
        return field.getSimpleType();
      }
    }

    // 默认使用长整型
    return "Long";
  }

  public String getPk() {
    for (FieldMeta field : fields) {
      if (field.getColumnMeta().getPrimaryKey()) {
        return field.getName();
      }
    }

    // 默认使用id
    return "id";
  }

  public String getFormWidth() {
    return tableConfig.getFormWidth();
  }

  public List<FieldMeta> getSearchFields() {
    return fields.stream()
        .filter(FieldMeta::getSearch)
        .sorted(Comparator.comparingInt(FieldMeta::getSearchSortRank))
        .toList();
  }

  public List<FieldMeta> getResultFields() {
    return fields.stream()
        .filter(FieldMeta::getResult)
        .sorted(Comparator.comparingInt(FieldMeta::getResultSortRank))
        .toList();
  }

  public List<FieldMeta> getFormFields() {
    return fields.stream()
        .filter(FieldMeta::getForm)
        .sorted(Comparator.comparingInt(FieldMeta::getFormSortRank))
        .toList();
  }

  public List<FieldMeta> getModelFields() {
    return fields.stream().filter(FieldMeta::getModel).toList();
  }

  public Object getValue(String key) {
    if (kvTableConfig == null) {
      if (tableConfig == null) {
        return null;
      }

      Map<String, Object> configMap = BeanUtil.beanToMap(tableConfig);
      kvTableConfig = Kv.create().set(configMap);
    }

    return kvTableConfig.get(key);
  }

  private ModuleConfig getModuleConfig(String moduleName) {
    Map<String, ModuleConfig> moduleConfigs = globalConfig.getModuleConfigs();
    return moduleConfigs.get(moduleName);
  }
}
